//
//  NetWorkSerializer.m
//  URLSessionTest
//
//  Created by mac on 2017/7/7.
//  Copyright © 2017年 yyb. All rights reserved.
//

#import "NetWorkSerializer.h"
@interface YYBAFQueryStringPair : NSObject
@property (readwrite, nonatomic, strong) id field;
@property (readwrite, nonatomic, strong) id value;

- (instancetype)initWithField:(id)field value:(id)value;

- (NSString *)URLEncodedStringValue;
@end

@implementation YYBAFQueryStringPair
NSString * YYBAFPercentEscapedStringFromString(NSString *string) {
    static NSString * const kYYBAFCharactersGeneralDelimitersToEncode = @":#[]@";
    static NSString * const kYYBAFCharactersSubDelimitersToEncode = @"!$&'()*+,;=";
    NSMutableCharacterSet * allowedCharacterSet = [[NSCharacterSet URLQueryAllowedCharacterSet] mutableCopy];
    [allowedCharacterSet removeCharactersInString:[kYYBAFCharactersGeneralDelimitersToEncode stringByAppendingString:kYYBAFCharactersSubDelimitersToEncode]];
    static NSUInteger const batchSize = 50;
    NSUInteger index = 0;
    NSMutableString *escaped = @"".mutableCopy;
    while (index < string.length) {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wgnu"
        NSUInteger length = MIN(string.length - index, batchSize);
#pragma GCC diagnostic pop
        NSRange range = NSMakeRange(index, length);
        // 为了避免之类的字符序列 👴🏻👮🏽
        range = [string rangeOfComposedCharacterSequencesForRange:range];
        NSString *substring = [string substringWithRange:range];
        NSString *encoded = [substring stringByAddingPercentEncodingWithAllowedCharacters:allowedCharacterSet];
        [escaped appendString:encoded];
        index += range.length;
    }
    return escaped;
}

- (instancetype)initWithField:(id)field value:(id)value {
    self = [super init];
    if (!self) {
        return nil;
    }
    self.field = field;
    self.value = value;
    return self;
}

- (NSString *)URLEncodedStringValue {
    if (!self.value || [self.value isEqual:[NSNull null]]) {
        return YYBAFPercentEscapedStringFromString([self.field description]);
    } else {
        return [NSString stringWithFormat:@"%@=%@", YYBAFPercentEscapedStringFromString([self.field description]), YYBAFPercentEscapedStringFromString([self.value description])];
    }
}

@end

FOUNDATION_EXPORT NSArray * YYBAFQueryStringPairsFromDictionary(NSDictionary *dictionary);
FOUNDATION_EXPORT NSArray * YYBAFQueryStringPairsFromKeyAndValue(NSString *key, id value);

NSString * YYBAFQueryStringFromParameters(NSDictionary *parameters) {
    NSMutableArray *mutablePairs = [NSMutableArray array];
    for (YYBAFQueryStringPair *pair in YYBAFQueryStringPairsFromDictionary(parameters)) {
        [mutablePairs addObject:[pair URLEncodedStringValue]];
    }
    return [mutablePairs componentsJoinedByString:@"&"];
}

NSArray * YYBAFQueryStringPairsFromDictionary(NSDictionary *dictionary) {
    return YYBAFQueryStringPairsFromKeyAndValue(nil, dictionary);
}

NSArray * YYBAFQueryStringPairsFromKeyAndValue(NSString *key, id value) {
    NSMutableArray *mutableQueryStringComponents = [NSMutableArray array];
    NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"description" ascending:YES selector:@selector(compare:)];
    if ([value isKindOfClass:[NSDictionary class]]) {
        NSDictionary *dictionary = value;
        // 排序字典键以确保一致的查询字符串排序,这是重要的,当反序列化可能模棱两可的序列,如一个字典数组
        for (id nestedKey in [dictionary.allKeys sortedArrayUsingDescriptors:@[ sortDescriptor ]]) {
            id nestedValue = dictionary[nestedKey];
            if (nestedValue) {
                [mutableQueryStringComponents addObjectsFromArray:YYBAFQueryStringPairsFromKeyAndValue((key ? [NSString stringWithFormat:@"%@[%@]", key, nestedKey] : nestedKey), nestedValue)];
            }
        }
    } else if ([value isKindOfClass:[NSArray class]]) {
        NSArray *array = value;
        for (id nestedValue in array) {
            [mutableQueryStringComponents addObjectsFromArray:YYBAFQueryStringPairsFromKeyAndValue([NSString stringWithFormat:@"%@[]", key], nestedValue)];
        }
    } else if ([value isKindOfClass:[NSSet class]]) {
        NSSet *set = value;
        for (id obj in [set sortedArrayUsingDescriptors:@[ sortDescriptor ]]) {
            [mutableQueryStringComponents addObjectsFromArray:YYBAFQueryStringPairsFromKeyAndValue(key, obj)];
        }
    } else {
        [mutableQueryStringComponents addObject:[[YYBAFQueryStringPair alloc] initWithField:key value:value]];
    }
    return mutableQueryStringComponents;
}

@interface NetWorkSerializer ()
@property(nonatomic, copy) NSString *urlString;
@property(nonatomic, copy) NSString *method;
@property(nonatomic, strong) NSDictionary <NSString *,id>* params;
@property(nonatomic, assign) BOOL UseJSONSerializer;
@end

@implementation NetWorkSerializer

- (instancetype)initWithURL:(NSString *)urlString
                  andMethod:(NSString *)method
       andUseJSONSerializer:(BOOL )useJSONSerializer
                  andParams:(NSDictionary <NSString *,id>*)params{
    if (self = [super init]) {
        self.urlString = urlString;
        self.method = method;
        self.params = params;
        self.UseJSONSerializer = useJSONSerializer;
    }
    return self;
}
- (NSMutableURLRequest *)buildRequest {
    NSMutableURLRequest *request;
    NSString *newURL = self.urlString;
    if ( [self.method isEqualToString:@"GET" ] && self.params.count > 0) {
        newURL = [newURL stringByAppendingString:@"?"];
        newURL = [newURL stringByAppendingString:YYBAFQueryStringFromParameters(self.params)];
    }
    request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:newURL]];
    request.HTTPMethod = self.method;
    request.HTTPBody = [self buildBody];
    if (self.params.count > 0) {
        [request addValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
    }
    return request;
}

- (NSData *)buildBody {
    NSData *HTTPBody;
    if (self.params.count > 0 && ![self.method isEqualToString:@"GET"]) {
        if (self.UseJSONSerializer) {
            HTTPBody = [YYBAFQueryStringFromParameters(self.params) dataUsingEncoding:NSUTF8StringEncoding];
        } else {
            HTTPBody = [NSJSONSerialization dataWithJSONObject:self.params options:NSJSONWritingPrettyPrinted error:nil];
        }
    }
    return HTTPBody;
}
@end
